package com.yituo.mjworld.core.utils;


import com.alibaba.fastjson.JSONObject;
import org.springframework.util.Base64Utils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类.
 * 主要功能:
 * 创建token
 * 校验token
 * 解析token
 *
 * @author Administrator
 */
public class JwtTool {
  private static final long serialVersionUID = 1L;
  /**
   * token加密用的私钥.
   * 也是token确认用户合法性的最重要的东西
   * 建议经常性更换,加大破解难度
   */
  private static String KEY = "xq123456";

  /**
   * 私有化JWTTool类.
   */
  private JwtTool() {

  }

  /**
   * 校验token合法性.
   * 用于特殊的接口
   * 比如给前端提供接口直接校验token是否有效
   */
  public static Map<String, Object> checkToken(String token) {
    Map<String, Object> result = new HashMap<String, Object>(2);
    if (token == null || "".equals(token)) {
      result.put("auth", false);
      result.put("msg", "token不存在");
      return result;
    }
    /**
     * .号是特殊字符,需要转义\\.才能正确分割
     */
    String[] jwt = token.split("\\.");
    if (jwt.length != 3) {
      result.put("auth", false);
      result.put("msg", "非法数据");
      return result;
    }
    String header = null;
    String payload = null;
    String signatureByToken = jwt[2];
    try {
      header = new String(Base64Utils.decodeFromUrlSafeString(jwt[0]), "UTF-8");
      payload = new String(Base64Utils.decodeFromUrlSafeString(jwt[1]), "UTF-8");
      String signatureByHash = hmacSha256((jwt[0] + "." + jwt[1]).getBytes(), KEY.getBytes());
      if (!signatureByHash.equals(signatureByToken)) {
        result.put("auth", false);
        result.put("msg", "token被篡改");
        return result;
      }
    } catch (IOException e) {
      result.put("auth", false);
      result.put("msg", "验证失败");
      return result;
    }
    result.put("auth", true);
    result.put("msg", "验证成功");
    return result;
  }

  /**
   * 常规的token校验.
   * 更详细的权限控制,参见ValidateConfig
   * 验证token合法性,和getToken的算法是反向算法 返回一个token合法性的JSON结果:{ msg:'',//相关信息
   * auth:false//授权是否成功的标志 }
   */
  public static JSONObject validateToken(String token) {
    JSONObject result = new JSONObject();
    if (token == null || "".equals(token)) {
      result.put("auth", false);
      result.put("msg", "token为空!");
      return result;
    }
    /**
     * .号是特殊字符,需要转义\\.才能正确分割
     */
    String[] jwt = token.split("\\.");
    if (jwt.length != 3) {
      result.put("auth", false);
      result.put("msg", "非法数据");
      return result;
    }
    String header = null;
    String payload = null;
    String signatureByToken = jwt[2];
    JSONObject payloadJson = null;
    // 有效时间://固定值
    long exp = 0;
    long iat = 0;
    String uid = "";
    try {
      header = new String(Base64Utils.decodeFromUrlSafeString(jwt[0]), "UTF-8");
      payload = new String(Base64Utils.decodeFromUrlSafeString(jwt[1]), "UTF-8");
      String signatureByHash = hmacSha256((jwt[0] + "." + jwt[1]).getBytes(), KEY.getBytes());
      if (!signatureByHash.equals(signatureByToken)) {
        result.put("auth", false);
        result.put("msg", "token被篡改");
        return result;
      }
      // 字符串转Json
      payloadJson = (JSONObject) JSONObject.parse(payload);
      exp = payloadJson.getLong("exp");
      iat = payloadJson.getLong("iat");
      uid = payloadJson.getString("uid");
      if (exp + iat <= System.currentTimeMillis()) {
        result.put("auth", false);
        result.put("msg", "token已过期!");
        return result;
      }
    } catch (IOException e) {
      result.put("auth", false);
      result.put("msg", "验证失败");
      return result;
    }
    result.put("auth", true);
    result.put("msg", "验证成功");
    result.put("payload", payload.toString());
    return result;
  }

  /**
   * 获取payload.
   *
   * @param token 欲解析的token
   * @return 返回token中的载体信息, 解析失败返回null
   */
  public static JSONObject getPayload(String token) {
    JSONObject payload = JwtTool.validateToken(token);
    if (payload != null) {
      return payload.getJSONObject("payload");
    } else {
      return null;
    }
  }


  /**
   * HMACSHA256算法:
   * 参考文档:http://blog.csdn.net/u012417984/article/details/49837411
   */
  private static String hmacSha256(byte[] data, byte[] key) {
    try {
      SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA256");
      Mac mac = Mac.getInstance("HmacSHA256");
      mac.init(signingKey);
      return byte2hex(mac.doFinal(data));
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    } catch (InvalidKeyException e) {
      e.printStackTrace();
    }
    return null;
  }

  /**
   * 字节数组转换为字符串
   */
  private static String byte2hex(byte[] b) {
    StringBuilder hs = new StringBuilder();
    String stmp;
    for (int n = 0; b != null && n < b.length; n++) {
      stmp = Integer.toHexString(b[n] & 0XFF);
      if (stmp.length() == 1) {
        hs.append('0');
      }
      hs.append(stmp);
    }
    return hs.toString().toUpperCase();
  }

  /**
   * 创建Token
   *
   * @param payload [JSONObject] 自定义载体,外部调用者如果有特殊需求,需要在payload中加入某些特殊信息,可以使用自定义payload
   * @return
   */
  public static String createToken(Payload payload) throws UnsupportedEncodingException {
    JSONObject header = new JSONObject();
    String headerStr = "";
    String payloadStr = "";
    String signature = "";
    //1.固定头部信息
    header.put("typ", "JWT");
    header.put("alg", "HS256");
    // 编码生成base64Url字符串
    headerStr = Base64Utils.encodeToUrlSafeString(header.toString().getBytes());

    // 2.要负载的信息(必须,校验token的最基本的常用字段)
    JSONObject payloadJson = (JSONObject) JSONObject.toJSON(payload);
    // 签发时间
    payloadStr = Base64Utils.encodeToUrlSafeString(payloadJson.toString().getBytes());
    //3.signature 生成
    signature = hmacSha256((headerStr + "." + payloadStr).getBytes(), KEY.getBytes());
    String token = headerStr + "." + payloadStr + "." + signature;
    return token;
  }
}